package com.zy.smartdb.adapter;

import com.zy.smartdb.enums.Relation;
import com.zy.smartdb.pojo.FieldInfo;
import com.zy.smartdb.pojo.FindParam;
import com.zy.smartdb.pojo.Where;
import com.zy.smartdb.util.CollectionUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.lang.Nullable;

import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Description:
 * @Author: zara
 * @Version V3.0
 * @Date: 2022/11/21 3:27 下午
 */

@Slf4j
@AllArgsConstructor
public class MyPreparedStatementAdapter<T> implements PreparedStatementCreator, PreparedStatementSetter, ResultSetExtractor<T> {

    private FindParam findParam;
    Map<String, FieldInfo> tableInfoMap;

    @Override
    public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
//        return con.prepareStatement("select count(*) from test");
        String sql = buildSelectSql(findParam);
        return con.prepareStatement(sql);
    }

    private String buildSelectSql(FindParam findParam) {
        String fields = "*";
        findParam = safetyHandle(findParam);
        List<String> fieldList = findParam.getFields();
        if (!CollectionUtil.isNullOrEmpty(fieldList)) {
            fields = CollectionUtil.join(fieldList);
        }
        String wheres = buildWheres(findParam);

        String sql = "select {0} from {1} where {2}";
        sql = sql.replace("{0}", fields)
                .replace("{1}", findParam.getTable())
                .replace("{2}", wheres);
        log.debug(sql);
        return sql;
    }

    private FindParam safetyHandle(FindParam findParam) {
        FindParam newFind = new FindParam();
        newFind.setTable(fillSpace(findParam.getTable()));
        List<String> fields = findParam.getFields();
        List<String> flist = new ArrayList<>(fields.size());
        for (String field : fields) {
            flist.add(fillSpace(field));
        }

        List<Where> wheres = findParam.getWheres();
        List<Where> wlist = new ArrayList<>(wheres.size());
        for (Where where : wheres) {
            Where wh = new Where();
            wh.setField(fillSpace(where.getField()));
            wh.setValue(fillSpace(where.getValue()));
            wh.setRelation(where.getRelation());
            wlist.add(wh);
        }

        newFind.setFields(flist);
        newFind.setWheres(wlist);

        return newFind;

    }

    //todo
    public static String fillSpace(String str) {
        return str;
    }


    private String buildWheres(FindParam findParam) {
        List<Where> whereList = findParam.getWheres();
//        Map<String, FieldInfo> tableInfoMap = getTableInfo(findParam.getTable());
        StringBuilder sb = new StringBuilder();
        sb.append(" 1 = 1 ");
        for (Where where : whereList) {
            String field = where.getField();
            sb.append(" and ");
            FieldInfo fieldInfo = tableInfoMap.get(field);
            String value = "?";
            switch (where.getRelation()) {
                case EQ:
                    sb.append(field).append(" = ").append(value);
                    break;
                case LT:
                    sb.append(field).append(" < ").append(value);
                    break;
                case GT:
                    sb.append(field).append(" > ").append(value);
                    break;
                case LIKE:
                    sb.append(field).append(" like ").append(value);
                    break;
                case ISNULL:
                    sb.append(field).append(" is null ");
                    break;

            }
        }
        return sb.toString();
    }

    @Override
    public void setValues(PreparedStatement ps) throws SQLException {
        List<Where> whereList = findParam.getWheres();
        int index = 1;
        for (Where where : whereList) {
            if (where.getRelation() != Relation.ISNULL) {
                ps.setObject(index++, where.getValue());

            }
        }
    }

    @Override
    public T extractData(ResultSet rs) throws SQLException, DataAccessException {
        List result = new ArrayList();
        while (rs.next()) {
            Map row = new HashMap();
            row.put(rs.getInt("id"), rs.getString("name"));
            result.add(row);
        }
        return (T)result;
    }

    public Map<String, Object> mapRow(ResultSet rs) throws SQLException {
        ResultSetMetaData rsmd = rs.getMetaData();
        int columnCount = rsmd.getColumnCount();
        Map<String, Object> mapOfColumnValues = new HashMap<>(columnCount);
        for (int i = 1; i <= columnCount; i++) {
            String column = JdbcUtils.lookupColumnName(rsmd, i);
            mapOfColumnValues.put(getColumnKey(column), getColumnValue(rs, i));
        }
        return mapOfColumnValues;
    }

    protected String getColumnKey(String columnName) {
        return columnName;
    }

    @Nullable
    protected Object getColumnValue(ResultSet rs, int index) throws SQLException {
        return JdbcUtils.getResultSetValue(rs, index);
    }


}
